//------------------------------------------------------------------------------
//!	@file dsp_file_info.c
//!	@brief
//!		DSP file info application
//!
//!	@Description
//!		This application reads a file and reports information about the file.
//!
//!	$Id: dsp_file_info.c 162 2013-03-03 07:09:28Z ac.verbeck@gmail.com $
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//	Copyright and Disclaimer Notice
//
//	Copyright (c) 2013 A.C. Verbeck
//
//	This file is part of DSP framework: an experimenters library
//
//	DSP framework is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	DSP framework is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with DSP framework.  If not, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <stdint.h>
#include <stdbool.h>

#include <popt.h>
#include <sndfile.h>

#include "bi.h"

//----------------------------------------------------------------------------
// Local defines
//----------------------------------------------------------------------------
#define OPT_INPUT			0x0001
#define OPT_ABOUT			0x0100
#define OPT_VERBOSE			0x0200

//----------------------------------------------------------------------------
// Local typedefs
//----------------------------------------------------------------------------
typedef struct FILE_FMT_TYPEtag {
	int		fmt_id;
	char*	fmt_string;
} FILE_FMT;

//----------------------------------------------------------------------------
// Local data
//----------------------------------------------------------------------------
static char*			FileName_In;	//  FileName: Input
static poptContext		optCon;

//----------------------------------------------------------------------------
// Local functions
//----------------------------------------------------------------------------
static void about(void);
static char* format_get(int fmt);
static char* subtype_get(int fmt);
static char* endian_get(int fmt);

//------------------------------------------------------------------------------
//	Main application
//
//	This application:
//	- opens the input file for reading
//	- creates the output file and prepares it for writing
//	- initializes the dsp effect under test
//	- sends the audio data block by block to the dsp effect
//	- writes the resulting audio to the output file
//	- clears out any remaining audio in the effect
//	- closes the input file
//	- closes the output file
//	- exits
//
//	Libraries:
//	- popt: used for argument processing
//	- sndfile: used for audio file I/O
//------------------------------------------------------------------------------
int	main(int argc, char ** argv)
{

//----------------------------------------------------------------------------
//  Declare local variables for main
//----------------------------------------------------------------------------
	SNDFILE *infile;
	SF_INFO sfinfo;

//----------------------------------------------------------------------------
//  Declare popt options
//----------------------------------------------------------------------------
	uint32_t	opt=0;
	int			idx=0;
	const char*	info_ptr;

	struct poptOption optionsTable[] = {
		{ "in",		'i', POPT_ARG_STRING, &FileName_In,	 'i', "input filename (required)",			NULL	},
		{ "about",	'a', POPT_ARG_NONE,	  NULL,			 'a', "display information about the app",	NULL	},
		{ "verbose",'v', POPT_ARG_NONE,	  NULL,			 'v', "enable verbose mode",				NULL	},
		POPT_AUTOHELP
		POPT_TABLEEND
	};

//----------------------------------------------------------------------------
//  Process arguments
//----------------------------------------------------------------------------
	optCon = poptGetContext(NULL, argc, (const char **)argv, optionsTable, 0);
	poptSetOtherOptionHelp(optCon, "[OPTIONS]*");
	if (argc == 1) {
		about();
	}
	while ((idx = poptGetNextOpt(optCon)) >= 0) {
		switch (idx) {
		case 'i':
			opt |= OPT_INPUT;
			break;
		case 'a':
			opt |= OPT_ABOUT;
			break;
		case 'v':
			opt |= OPT_VERBOSE;
			break;
		}
	}
	if (idx < -1) {
		fprintf(stderr, "%s: %s\n",
		        poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
		        poptStrerror(idx));
		exit(opt);
	}
	if (opt & OPT_ABOUT) {
		bldinfoPrintBldInfo();
		exit(0);
	}
	if ((opt & OPT_INPUT) == 0) {
		printf("Input file required.\n");
		exit(0);
	}
	if (opt & OPT_VERBOSE) {
		printf("Verbose option: active\n");
	}

//------------------------------------------------------------------------------
//	Open the input file
//------------------------------------------------------------------------------
	memset(&sfinfo, 0, sizeof (sfinfo));
	if ((infile = sf_open(FileName_In, SFM_READ, &sfinfo)) == NULL) {
		printf("Error : Not able to open input file '%s'\n", FileName_In);
		sf_close(infile);
		exit(1);
	};

//------------------------------------------------------------------------------
//	Get the file information and report
//------------------------------------------------------------------------------
	fprintf(stdout, "Filename: %s\n",FileName_In);
	fprintf(stdout, "      frames: %I64d (%I64d seconds)\n",	sfinfo.frames, sfinfo.frames/sfinfo.samplerate);
	fprintf(stdout, "  samplerate: %d\n",	sfinfo.samplerate);
	fprintf(stdout, "    channels: %d\n",	sfinfo.channels);

	fprintf(stdout, "      format:\n");
	fprintf(stdout, "      -- hex: %08x\n",	sfinfo.format);
	fprintf(stdout, "      -- fmt: %s\n",	format_get(sfinfo.format));
	fprintf(stdout, "      -- sub: %s\n", 	subtype_get(sfinfo.format));
	fprintf(stdout, "      -- end: %s\n", 	endian_get(sfinfo.format));

	fprintf(stdout, "    sections: %d\n",	sfinfo.sections);
	fprintf(stdout, "    seekable: %d\n",	sfinfo.seekable);

	if (opt & OPT_VERBOSE) {
		fprintf(stdout, "Additional Miscellaneous Information:\n");

		info_ptr = sf_get_string(infile, SF_STR_TITLE);							//	Get file information
		info_ptr = (info_ptr == NULL) ? "None" : info_ptr;						//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Title: %s\n", info_ptr);							//	print it

		info_ptr = sf_get_string(infile, SF_STR_COPYRIGHT);						//	Get file information
		info_ptr = (info_ptr == NULL) ? "No copyright" : info_ptr;				//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Copyright: %s\n", info_ptr);						//	print it

		info_ptr = sf_get_string(infile, SF_STR_ARTIST);						//	Get file information
		info_ptr = (info_ptr == NULL) ? "None" : info_ptr;						//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Artist: %s\n", info_ptr);							//	print it

		info_ptr = sf_get_string(infile, SF_STR_COMMENT);						//	Get file information
		info_ptr = (info_ptr == NULL) ? "None" : info_ptr;						//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Comment: %s\n", info_ptr);							//	print it

		info_ptr = sf_get_string(infile, SF_STR_DATE);							//	Get file information
		info_ptr = (info_ptr == NULL) ? "None" : info_ptr;						//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Date: %s\n", info_ptr);							//	print it

		info_ptr = sf_get_string(infile, SF_STR_ALBUM);							//	Get file information
		info_ptr = (info_ptr == NULL) ? "None" : info_ptr;						//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Album: %s\n", info_ptr);							//	print it

		info_ptr = sf_get_string(infile, SF_STR_LICENSE);						//	Get file information
		info_ptr = (info_ptr == NULL) ? "None" : info_ptr;						//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    License: %s\n", info_ptr);							//	print it

		info_ptr = sf_get_string(infile, SF_STR_TRACKNUMBER);					//	Get file information
		info_ptr = (info_ptr == NULL) ? "No track number" : info_ptr;			//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Track Number: %s\n", info_ptr);					//	print it

		info_ptr = sf_get_string(infile, SF_STR_GENRE);							//	Get file information
		info_ptr = (info_ptr == NULL) ? "No genre" : info_ptr;					//	Set the info_ptr to NULL if no string
		fprintf(stdout, "    Genre: %s\n", info_ptr);							//	print it
	}

//------------------------------------------------------------------------------
//	Close the file
//------------------------------------------------------------------------------
	sf_close(infile);

	return 0;
}

//------------------------------------------------------------------------------
//!	@brief
//!		Display about
//!
//!	@param
//!		none
//!
//!	@return
//!		none
//------------------------------------------------------------------------------
static void about(void)
{
	bldinfoPrintBldInfo();
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
}

//------------------------------------------------------------------------------
//!	@brief
//!		SF file format values
//!
//!	@Description
//!		This structure contains all possible file formats
//!
//------------------------------------------------------------------------------
FILE_FMT fmt_type_tab[] = {
	{ SF_FORMAT_WAV,	"Microsoft WAV format (little endian default)."	},
	{ SF_FORMAT_AIFF,	"Apple/SGI AIFF format (big endian)."			},
	{ SF_FORMAT_AU,		"Sun/NeXT AU format (big endian)."				},
	{ SF_FORMAT_RAW,	"RAW PCM data."									},
	{ SF_FORMAT_PAF,	"Ensoniq PARIS file format."					},
	{ SF_FORMAT_SVX,	"Amiga IFF / SVX8 / SV16 format."				},
	{ SF_FORMAT_NIST,	"Sphere NIST format."							},
	{ SF_FORMAT_VOC,	"VOC files."									},
	{ SF_FORMAT_IRCAM,	"Berkeley/IRCAM/CARL"							},
	{ SF_FORMAT_W64,	"Sonic Foundry's 64 bit RIFF/WAV"				},
	{ SF_FORMAT_MAT4,	"Matlab (tm) V4.2 / GNU Octave 2.0"				},
	{ SF_FORMAT_MAT5,	"Matlab (tm) V5.0 / GNU Octave 2.1"				},
	{ SF_FORMAT_PVF,	"Portable Voice Format"							},
	{ SF_FORMAT_XI,		"Fasttracker 2 Extended Instrument"				},
	{ SF_FORMAT_HTK,	"HMM Tool Kit format"							},
	{ SF_FORMAT_SDS,	"Midi Sample Dump Standard"						},
	{ SF_FORMAT_AVR,	"Audio Visual Research"							},
	{ SF_FORMAT_WAVEX,	"MS WAVE with WAVEFORMATEX"						},
	{ SF_FORMAT_SD2,	"Sound Designer 2"								},
	{ SF_FORMAT_FLAC,	"FLAC lossless file format"						},
	{ SF_FORMAT_CAF,	"Core Audio File format"						},
	{ SF_FORMAT_WVE,	"Psion WVE format"								},
	{ SF_FORMAT_OGG,	"Xiph OGG container"							},
	{ SF_FORMAT_MPC2K,	"Akai MPC 2000 sampler"							},
	{ SF_FORMAT_RF64,	"RF64 WAV file"									},
};
static const int fmt_type_tab_ct = sizeof(fmt_type_tab)/sizeof(fmt_type_tab[0]);

//------------------------------------------------------------------------------
//!	@brief
//!		SF file format values
//!		format_get returns a string version of the format value
//!
//!	@param
//!		int fmt: this is from the sfinfo structure
//!
//!	@return
//!		char* to an ASCII file description
//------------------------------------------------------------------------------
static char* format_get(int fmt)
{
	int		fmt_type	= fmt & SF_FORMAT_TYPEMASK;
	char*	r_str		= NULL;

	for (int i=0; i<fmt_type_tab_ct; i++) {
		if (fmt_type == fmt_type_tab[i].fmt_id) {
			r_str = fmt_type_tab[i].fmt_string;
			break;
		}
	}

	return r_str;
}

//------------------------------------------------------------------------------
//!	@brief
//!		SF file sub-format values
//!
//!	@Description
//!		This structure contains all possible sub-format types
//!
//------------------------------------------------------------------------------
FILE_FMT sub_type_tab[] = {
	{	SF_FORMAT_PCM_S8,		"Signed 8 bit data"								},
	{	SF_FORMAT_PCM_16,		"Signed 16 bit data"							},
	{	SF_FORMAT_PCM_24,		"Signed 24 bit data"							},
	{	SF_FORMAT_PCM_32,		"Signed 32 bit data"							},
	{	SF_FORMAT_PCM_U8,		"Unsigned 8 bit data (WAV and RAW only)"		},
	{	SF_FORMAT_FLOAT,		"32 bit float data"								},
	{	SF_FORMAT_DOUBLE,		"64 bit float data"								},
	{	SF_FORMAT_ULAW,			"U-Law encoded."								},
	{	SF_FORMAT_ALAW,			"A-Law encoded."								},
	{	SF_FORMAT_IMA_ADPCM,	"IMA ADPCM."									},
	{	SF_FORMAT_MS_ADPCM,		"Microsoft ADPCM."								},
	{	SF_FORMAT_GSM610,		"GSM 6.10 encoding."							},
	{	SF_FORMAT_VOX_ADPCM,	"OKI / Dialogix ADPCM"							},
	{	SF_FORMAT_G721_32,		"32kbs G721 ADPCM encoding."					},
	{	SF_FORMAT_G723_24,		"24kbs G723 ADPCM encoding."					},
	{	SF_FORMAT_G723_40,		"40kbs G723 ADPCM encoding."					},
	{	SF_FORMAT_DWVW_12,		"12 bit Delta Width Variable Word encoding."	},
	{	SF_FORMAT_DWVW_16,		"16 bit Delta Width Variable Word encoding."	},
	{	SF_FORMAT_DWVW_24,		"24 bit Delta Width Variable Word encoding."	},
	{	SF_FORMAT_DWVW_N,		"N bit Delta Width Variable Word encoding."		},
	{	SF_FORMAT_DPCM_8,		"8 bit differential PCM (XI only)"				},
	{	SF_FORMAT_DPCM_16,		"16 bit differential PCM (XI only)"				},
	{	SF_FORMAT_VORBIS,		"Xiph Vorbis encoding."							},
};
static const int sub_type_tab_ct = sizeof(sub_type_tab)/sizeof(sub_type_tab[0]);

//------------------------------------------------------------------------------
//!	@brief
//!		subtype_get returns a string version of the subtype value
//!		Uses SF file sub-format value array
//!
//!	@param
//!		int fmt: this is from the sfinfo structure
//!
//!	@return
//!		char* to an ASCII file description
//------------------------------------------------------------------------------
static char* subtype_get(int fmt)
{
	int		fmt_sub	= fmt & SF_FORMAT_SUBMASK;
	char*	r_str	= NULL;

	for (int i=0; i<sub_type_tab_ct; i++) {
		if (fmt_sub == sub_type_tab[i].fmt_id) {
			r_str = sub_type_tab[i].fmt_string;
			break;
		}
	}

	return r_str;
}

//------------------------------------------------------------------------------
//!	@brief
//!		SF file endian-format values
//!
//!	@Description
//!		This structure contains all possible endian types
//!
//------------------------------------------------------------------------------
FILE_FMT end_type_tab[] = {
	{	SF_ENDIAN_FILE,	 	"Default file endian-ness."	},
	{	SF_ENDIAN_LITTLE,	"Force little endian-ness."	},
	{	SF_ENDIAN_BIG,	 	"Force big endian-ness."	},
	{	SF_ENDIAN_CPU,	 	"Force CPU endian-ness."	},
};
static const int end_type_tab_ct = sizeof(sub_type_tab)/sizeof(sub_type_tab[0]);

//------------------------------------------------------------------------------
//!	@brief
//!		endian_get returns a string version of the endian value
//!		Uses SF file endian value array
//!
//!	@param
//!		int fmt: this is from the sfinfo structure
//!
//!	@return
//!		char* to an ASCII file description
//------------------------------------------------------------------------------
static char* endian_get(int fmt)
{
	int		fmt_end	= fmt & SF_FORMAT_ENDMASK;
	char*	r_str	= NULL;

	for (int i=0; i<end_type_tab_ct; i++) {
		if (fmt_end == end_type_tab[i].fmt_id) {
			r_str = end_type_tab[i].fmt_string;
			break;
		}
	}

	return r_str;
}


//
//	End: dsp_file_info.c
//